using System;
using System.Collections.Generic;
using System.Text;

using tp1.Entidades;

namespace tp1
{
    class Clipping
    {
        #region Clipping Poligono
        public static List<Punto> RecortarPoligono(List<Punto> listado,Viewport viewport)
        {
            List<Punto> listaClip;

            Punto pia = new Punto(viewport.Xvi, viewport.Yva);
            Punto pda = new Punto(viewport.Xvd, viewport.Yva);
            Punto pdb = new Punto(viewport.Xvd, viewport.Yvb);
            Punto pib = new Punto(viewport.Xvi, viewport.Yvb);

            listaClip = Recortar(listado, pia, pda);
            listaClip = Recortar(listaClip, pda, pdb);
            listaClip = Recortar(listaClip, pdb, pib);
            listaClip = Recortar(listaClip, pib, pia);

            return listaClip;
        }

        private static List<Punto> Recortar(List<Punto> listado, Punto vertice1, Punto vertice2)
        {
            List<Punto> listadoClip = new List<Punto>();

            if (listado.Count == 0)
                return listadoClip;

            Punto s = listado[listado.Count-1];

            foreach (Punto p in listado)
            {
                if (EsInterior(p, vertice1, vertice2))
                {
                    if (EsInterior(s, vertice1, vertice2))
                    {
                        listadoClip.Add(p);
                    }
                    else
                    {
                        listadoClip.Add(Interseccion(s, p, vertice1, vertice2));
                        listadoClip.Add(p);
                    }
                }
                else if (EsInterior(s, vertice1, vertice2))
                {
                    listadoClip.Add(Interseccion(s, p, vertice1, vertice2));
                }

                s = p;
            }

            return listadoClip;
        }

        private static bool EsInterior(Punto p, Punto vertice1, Punto vertice2)
        {
            if (vertice2.Xint > vertice1.Xint)	//inferior
            {
                if (p.Yint >= vertice1.Yint)
                {
                    return true;
                }
            }
            if (vertice2.Xint < vertice1.Xint)  //superior
            {
                if (p.Yint <= vertice1.Yint)
                {
                    return true;
                }
            }
            if (vertice2.Yint > vertice1.Yint) //derecha
            {
                if (p.Xint <= vertice2.Xint)
                {
                    return true;
                }
            }
            if (vertice2.Yint < vertice1.Yint) //izquierda
            {
                if (p.Xint >= vertice2.Xint)
                {
                    return true;
                }
            }
            return false;
        }

        private static Punto Interseccion(Punto s, Punto p, Punto vertice1, Punto vertice2)
        {
            double x, y;
            if (vertice1.Yint == vertice2.Yint) //Limite vertical
	        {
		        y = vertice1.Y;
		        x = s.X + (vertice1.Y - s.Y) * (p.X-s.X)/(p.Y-s.Y);
	        }
	        else			//Limite vertical
	        {
		        x = vertice1.X;
		        y = s.Y + (vertice1.X-s.X) *	(p.Y-s.Y)/(p.X-s.X);
	        }
            return new Punto(x, y);
        }

        #endregion

        #region Clipping Rectas
        public static List<Punto> RecortarSegmento(Punto p1, Punto p2, Viewport viewport)
        {
            List<Punto> listaClip = new List<Punto>();
            Punto punto1 = new Punto(p1.X, p1.Y);
            Punto punto2 = new Punto(p2.X, p2.Y);

            Punto pia = new Punto(viewport.Xvi, viewport.Yvb);
            //Punto pda = new Punto(viewport.Xvd, viewport.Yva);
            Punto pdb = new Punto(viewport.Xvd, viewport.Yva);
            //Punto pib = new Punto(viewport.Xvi, viewport.Yvb);

            double tMin = 0;
            double tMax = 1;
            double dx = p2.X - p1.X;
            double dy = p2.Y - p1.Y;

            if (ClipTest(-dx, (p1.X-pia.X), ref tMin, ref tMax))
            {
                if (ClipTest(dx, (pdb.X - p1.X), ref tMin, ref tMax))
                {
                    if (ClipTest(-dy, (p1.Y - pdb.Y), ref tMin, ref tMax))
                    {
                        if (ClipTest(dy, (pia.Y - p1.Y), ref tMin, ref tMax))
                        {
                            if (tMax < 1)
                            {
                                punto2.X = p1.X + tMax*dx;
                                punto2.Y = p1.Y + tMax*dy;
                            }
                            if (tMin > 0)
                            {
                                punto1.X = p1.X + tMin*dx;
                                punto1.Y = p1.Y + tMin*dy;
                            }
                            listaClip.Add(punto1);
                            listaClip.Add(punto2);
                        }
                    }
                }
            }

            return listaClip;
        }

        private static bool ClipTest(double p, double q, ref double tMin, ref double tMax)
        {
            if (p == 0)
            {
                if (q < 0) return false;
            }
            else
            {
                double r = q / p;
                if (p < 0)
                {
                    if (r > tMax) return false;
                    else if (r>tMin) tMin = r;
                }
                else
                {
                    if (r < tMin) return false;
                    else if (r<tMax) tMax = r;
                }
            }
            return true;
        }

        #endregion
    }
}
